#pragma once

#include <windows.h>
#include <functional>
#include <stdio.h>
#include <string>
#include "SharedMemory.hpp"

namespace CppWinApi::Windows
{
    enum EProcessRWLockScope
    {
        Local,
        Global
    };

    struct ReadWriteLockData
    {
        LONG ActiveReaders;
    };

    template <EProcessRWLockScope SCOPE>
    struct SharedMemoryScopeType
    {
        // 启用本地共享内存
        static constexpr ESharedMemoryScope Scope = ESharedMemoryScope::Local;
    };

    template <>
    struct SharedMemoryScopeType<EProcessRWLockScope::Global>
    {
        // 启用全局共享内存
        static constexpr ESharedMemoryScope Scope = ESharedMemoryScope::Global;
    };

    template <const char *UUID, const int MAX_ACTIVE_READER = 10, const EProcessRWLockScope SCOPE = EProcessRWLockScope::Local>
    /// @brief 进程读写锁
    class ProcessReadWriteLock
    {
        friend class AutoReadLock;
        friend class AutoWriteLock;

        /// @brief 读写锁实例
    public:
        static ProcessReadWriteLock<UUID, MAX_ACTIVE_READER, SCOPE> &GetInstance()
        {
            static ProcessReadWriteLock<UUID, MAX_ACTIVE_READER, SCOPE> gInstance;
            return gInstance;
        }

    private:
        /// @brief 共享数据
        SharedMemory<UUID, ReadWriteLockData, SharedMemoryScopeType<SCOPE>::Scope> &mSharedData;

        /// @brief 写锁定
        HANDLE mWriteLock;

        /// @brief 读锁顶
        HANDLE mReadLock;

        /// @brief 读者限定信号量
        HANDLE mReadSemaphore; // 读者数量控制信号量（最大10）

        /// @brief 类型 id
        std::string mClassId;

        /**
         * @brief 生成互斥量/信号量的名称
         * @param key 唯一 uuid
         * @return 完整的互斥量/信号量名称
         */
        std::string MakeName(const std::string name)
        {
            if (SCOPE == EProcessRWLockScope::Global)
            {
                return std::string("Global\\") + UUID + "_" + mClassId + "_" + name;
            }
            else
            {
                return std::string("Local\\") + UUID + "_" + mClassId + "_" + name;
            }
        }

        /// @brief 获取读锁
        void AcquireRead()
        {
            // 1. 获取读信号量许可（若已达10则阻塞）
            WaitForSingleObject(mReadSemaphore, INFINITE);

            // 2. 优先写检查
            WaitForSingleObject(mWriteLock, INFINITE);
            ReleaseMutex(mWriteLock);

            // 3. 原子递增活跃读者计数
            LONG temp =
                mSharedData.Action([](ReadWriteLockData *data)
                                   { 
                data->ActiveReaders++; 
                return data->ActiveReaders; });

            // 4. 首次读取，锁定读锁
            if (temp == 1)
            {
                // 获取读锁
                WaitForSingleObject(mReadLock, INFINITE);
            }
        }

        /// @brief 释放读锁
        void ReleaseRead()
        {
            // 1. 原子递减活跃读者计数
            LONG temp =
                mSharedData.Action([](ReadWriteLockData *data)
                                   {
                data->ActiveReaders--;
                return data->ActiveReaders; });

            // 2. 末位读者释放写锁
            if (temp == 0)
            {
                // 释放读锁
                ReleaseMutex(mReadLock);
            }

            // 3. 释放读信号量许可
            ReleaseSemaphore(mReadSemaphore, 1, NULL);
        }

        /// @brief 获取写锁
        void AcquireWrite()
        {
            // 1. 写锁定，其他进程禁止读取
            WaitForSingleObject(mWriteLock, INFINITE);
            // 2. 写锁顶，等待所有读进程读取完成
            WaitForSingleObject(mReadLock, INFINITE);
        }

        /// @brief 释放写锁
        void ReleaseWrite()
        {
            // 1. 释放读锁
            ReleaseMutex(mReadLock);
            // 2. 释放写锁
            ReleaseMutex(mWriteLock);
        }

        ProcessReadWriteLock() : mSharedData(SharedMemory<UUID, ReadWriteLockData, SharedMemoryScopeType<SCOPE>::Scope>::GetInstance())
        {
            mClassId = "7256ef32-f549-4bdf-8a41-920f009c4f02";
            auto writeLockName = MakeName("write_lock");
            auto readLockName = MakeName("read_lock");
            auto semaphoreName = MakeName("semaphore");
            mWriteLock = CreateMutexA(NULL, FALSE, writeLockName.c_str());

            if (mWriteLock == NULL)
            {
                throw std::runtime_error("Failed to create write mutex");
            }

            mReadLock = CreateMutexA(NULL, FALSE, readLockName.c_str());

            if (mReadLock == NULL)
            {
                throw std::runtime_error("Failed to create read mutex");
            }
            mReadSemaphore = ::CreateSemaphoreA(NULL, MAX_ACTIVE_READER, MAX_ACTIVE_READER, semaphoreName.c_str());

            if (mReadSemaphore == NULL)
            {
                throw std::runtime_error("Failed to create read semaphore");
            }
        }

    public:
        ProcessReadWriteLock(const ProcessReadWriteLock &) = delete;
        ProcessReadWriteLock &operator=(const ProcessReadWriteLock &) = delete;

        ~ProcessReadWriteLock()
        {
            if (mReadSemaphore)
                ::CloseHandle(mReadSemaphore);
            if (mReadLock)
                ::CloseHandle(mReadLock);
            if (mWriteLock)
                ::CloseHandle(mWriteLock);
        }

        /// @brief 执行一个同步读
        /// @param lambda 读取逻辑
        template <
            typename TLambdaFunc,
            typename... TArgs,
            typename TResult = decltype(std::declval<TLambdaFunc>()(std::declval<TArgs>()...)),
            // TLambdaFunc 约束，必须是 class(lambda 就是一个匿名类)，返回值类型必须是 TResult
            typename = std::enable_if_t<std::is_class_v<TLambdaFunc> && std::is_invocable_r_v<TResult, TLambdaFunc, TArgs...>>>
        TResult ReadAction(TLambdaFunc &&lambda, TArgs... args)
        {
            AutoReadLock tmp(*this);

            // 编译时分支：根据返回值类型选择执行方式
            if constexpr (std::is_void_v<TResult>)
            {
                // void 特化：无返回值
                std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
                // 注意：此处不需要 return 语句
            }
            else
            {
                // 非 void 类型：转发返回值
                return std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
                // 编译器会确保所有路径都有返回值（否则报错）
            }

            // 注：非 void 分支必须通过 return 返回，此处不会执行
        }

        /// @brief 执行一个同步写
        /// @param lambda 写入逻辑
        template <
            typename TLambdaFunc,
            typename... TArgs,
            typename TResult = decltype(std::declval<TLambdaFunc>()(std::declval<TArgs>()...)),
            // TLambdaFunc 约束，必须是 class(lambda 就是一个匿名类)，返回值类型必须是 TResult
            typename = std::enable_if_t<std::is_class_v<TLambdaFunc> && std::is_invocable_r_v<TResult, TLambdaFunc, TArgs...>>>
        TResult WriteAction(TLambdaFunc &&lambda, TArgs... args)
        {
            AutoWriteLock tmp(*this);

            // 编译时分支：根据返回值类型选择执行方式
            if constexpr (std::is_void_v<TResult>)
            {
                // void 特化：无返回值
                std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
                // 注意：此处不需要 return 语句
            }
            else
            {
                // 非 void 类型：转发返回值
                return std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
                // 编译器会确保所有路径都有返回值（否则报错）
            }

            // 注：非 void 分支必须通过 return 返回，此处不会执行
        }

        class AutoReadLock
        {
        public:
            explicit AutoReadLock(ProcessReadWriteLock &lock) : m_lock(lock)
            {
                m_lock.AcquireRead();
            }
            ~AutoReadLock()
            {
                m_lock.ReleaseRead();
            }
            AutoReadLock(const AutoReadLock &) = delete;
            AutoReadLock &operator=(const AutoReadLock &) = delete;

        private:
            ProcessReadWriteLock &m_lock;
        };

        class AutoWriteLock
        {
        public:
            explicit AutoWriteLock(ProcessReadWriteLock &lock) : m_lock(lock)
            {
                m_lock.AcquireWrite();
            }
            ~AutoWriteLock()
            {
                m_lock.ReleaseWrite();
            }
            AutoWriteLock(const AutoWriteLock &) = delete;
            AutoWriteLock &operator=(const AutoWriteLock &) = delete;

        private:
            ProcessReadWriteLock &m_lock;
        };
    };
}